Rework coordinate transformation to be based on root window
authorKristian Rietveld <kris@gtk.org>
Sun, 25 Oct 2009 20:36:56 +0000 (21:36 +0100)
committerKristian Rietveld <kris@gtk.org>
Mon, 26 Oct 2009 08:52:54 +0000 (09:52 +0100)
The root window contains all the monitors attached to a Mac.  The
coordinate transformation now both translates the x and y coordinate,
translating it from the Cocoa monitor coordinate space to the GDK
coordinate space.  How monitors are laid out in the root window differs
between Cocoa and GDK, which is why it is important to translate based
on the root window to get multi monitor setups to work properly.

We have replaced the old y coordinate transformation function with
new functions that translate both the x and y coordinate.

When creating new toplevels, we have to determine the Cocoa screen on
which the toplevel should appear and translate the coordinates according
to that screen.

This change also fixes event handling in case there is a monitor left
of the screen containing the menu bar.  In such a case all coordinates
on the left monitor are negative.  Event handling broke, because of
_gdk_quartz_window_find_child() checking bounds.  Now that coordinates
are always properly translated to GDK coordinate space, in which negative
coordinates do never occur, the checks here will work properly.

gdk/quartz/GdkQuartzWindow.c
gdk/quartz/gdkevents-quartz.c
gdk/quartz/gdkprivate-quartz.h
gdk/quartz/gdkscreen-quartz.c
gdk/quartz/gdkscreen-quartz.h
gdk/quartz/gdkwindow-quartz.c

index a974bdaee3c31af94bb2cfd5a949486741ff6d18..d38fe22a148a01d07fbfa8a073f97a3017ed06f6 100644 (file)
   GdkWindowObject *private = (GdkWindowObject *)window;
   GdkEvent *event;
 
-  private->x = content_rect.origin.x;
-  private->y = _gdk_quartz_window_get_inverted_screen_y (content_rect.origin.y + content_rect.size.height);
+  _gdk_quartz_window_xy_to_gdk_xy (content_rect.origin.x,
+                                   content_rect.origin.y + content_rect.size.height,
+                                   &private->x, &private->y);
 
   /* Synthesize a configure event */
   event = gdk_event_new (GDK_CONFIGURE);
   _gdk_event_queue_append (gdk_display_get_default (), event);
 }
 
--(id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
+-(id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag screen:(NSScreen *)screen
 {
   self = [super initWithContentRect:contentRect
                          styleMask:styleMask
                            backing:backingType
-                             defer:flag];
+                             defer:flag
+                             screen:screen];
 
   [self setAcceptsMouseMovedEvents:YES];
   [self setDelegate:self];
@@ -506,16 +508,18 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
   NSPoint point = [sender draggingLocation];
   NSPoint screen_point = [self convertBaseToScreen:point];
   GdkEvent event;
+  int gx, gy;
 
   update_context_from_dragging_info (sender);
+  _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
 
   event.dnd.type = GDK_DRAG_MOTION;
   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
   event.dnd.send_event = FALSE;
   event.dnd.context = current_context;
   event.dnd.time = GDK_CURRENT_TIME;
-  event.dnd.x_root = screen_point.x;
-  event.dnd.y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
+  event.dnd.x_root = gx;
+  event.dnd.y_root = gy;
 
   (*_gdk_event_func) (&event, _gdk_event_data);
 
@@ -529,16 +533,18 @@ update_context_from_dragging_info (id <NSDraggingInfo> sender)
   NSPoint point = [sender draggingLocation];
   NSPoint screen_point = [self convertBaseToScreen:point];
   GdkEvent event;
+  int gy, gx;
 
   update_context_from_dragging_info (sender);
+  _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &gx, &gy);
 
   event.dnd.type = GDK_DROP_START;
   event.dnd.window = g_object_ref ([[self contentView] gdkWindow]);
   event.dnd.send_event = FALSE;
   event.dnd.context = current_context;
   event.dnd.time = GDK_CURRENT_TIME;
-  event.dnd.x_root = screen_point.x;
-  event.dnd.y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
+  event.dnd.x_root = gx;
+  event.dnd.y_root = gy;
 
   (*_gdk_event_func) (&event, _gdk_event_data);
 
index 54baef7f524bcbe587eeb8e913245831daa04660..24044b355c5687615ab9e0ca3057960b0ef5bdf7 100644 (file)
@@ -465,8 +465,7 @@ find_window_for_ns_event (NSEvent *nsevent,
   *x = point.x;
   *y = private->height - point.y;
 
-  *x_root = screen_point.x;
-  *y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
+  _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, x_root, y_root);
 
   event_type = [nsevent type];
 
index 52834b36d5d7745af8e6a4fa75e59f4bbd83a9c3..258d1f8c70c099307d15a5adcdb08e7eea1b653e 100644 (file)
@@ -138,7 +138,17 @@ void _gdk_quartz_colormap_get_rgba_from_pixel (GdkColormap *colormap,
 /* Window */
 gboolean    _gdk_quartz_window_is_ancestor          (GdkWindow *ancestor,
                                                      GdkWindow *window);
-gint       _gdk_quartz_window_get_inverted_screen_y (gint       y);
+void       _gdk_quartz_window_gdk_xy_to_xy          (gint       gdk_x,
+                                                     gint       gdk_y,
+                                                     gint      *ns_x,
+                                                     gint      *ns_y);
+void       _gdk_quartz_window_xy_to_gdk_xy          (gint       ns_x,
+                                                     gint       ns_y,
+                                                     gint      *gdk_x,
+                                                     gint      *gdk_y);
+void       _gdk_quartz_window_nspoint_to_gdk_xy     (NSPoint    point,
+                                                     gint      *x,
+                                                     gint      *y);
 GdkWindow *_gdk_quartz_window_find_child            (GdkWindow *window,
                                                     gint       x,
                                                     gint       y);
index c9b45366e64bd9585242390c12a91e934c35ca6b..4afeb51975aae8c7cebc8f4c8e7f62d8064de711 100644 (file)
@@ -129,6 +129,8 @@ gdk_screen_quartz_calculate_layout (GdkScreenQuartz *screen)
    */
   screen->width = 0;
   screen->height = 0;
+  screen->min_x = 0;
+  screen->min_y = 0;
 
   for (i = 0; i < [array count]; i++)
     {
@@ -136,6 +138,8 @@ gdk_screen_quartz_calculate_layout (GdkScreenQuartz *screen)
 
       screen->width += rect.size.width;
       screen->height = MAX (screen->height, rect.size.height);
+      screen->min_x = MIN (screen->min_x, rect.origin.x);
+      screen->min_y = MIN (screen->min_y, rect.origin.y);
     }
 
   screen->n_screens = [array count];
@@ -162,7 +166,7 @@ gdk_screen_quartz_calculate_layout (GdkScreenQuartz *screen)
       nsscreen = [array objectAtIndex:i];
       rect = [nsscreen frame];
 
-      screen->screen_rects[i].x = rect.origin.x;
+      screen->screen_rects[i].x = rect.origin.x - screen->min_x;
       screen->screen_rects[i].width = rect.size.width;
       screen->screen_rects[i].height = rect.size.height;
 
index 370e876984310f40758228b7318d1de577397804..82da95cf1c2f02ec42a69b92081ce74879b94023 100644 (file)
@@ -40,6 +40,10 @@ struct _GdkScreenQuartz
   GdkDisplay *display;
   GdkColormap *default_colormap;
 
+  /* Origin of "root window" in Cocoa coordinates */
+  gint min_x;
+  gint min_y;
+
   gint width;
   gint height;
 
index 24057b50eb04adcef5987ec47b66b0e2ce475f93..f60cc79779413fc64c1f768e31a45375d4ceeb04 100644 (file)
@@ -25,6 +25,7 @@
 #include "gdk.h"
 #include "gdkwindowimpl.h"
 #include "gdkprivate-quartz.h"
+#include "gdkscreen-quartz.h"
 #include "gdkinputprivate.h"
 
 static gpointer parent_class;
@@ -557,6 +558,7 @@ _gdk_quartz_window_debug_highlight (GdkWindow *window, gint number)
 {
   GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
   gint x, y;
+  gint gx, gy;
   GdkWindow *toplevel;
   gint tx, ty;
   static NSWindow *debug_window[10];
@@ -585,9 +587,10 @@ _gdk_quartz_window_debug_highlight (GdkWindow *window, gint number)
   x += tx;
   y += ty;
 
-  rect = NSMakeRect (x,
-                     _gdk_quartz_window_get_inverted_screen_y (y + private->height),
-                     private->width, private->height);
+  _gdk_quartz_window_gdk_xy_to_xy (x, y + private->height,
+                                   &gx, &gy);
+
+  rect = NSMakeRect (gx, gy, private->width, private->height);
 
   if (debug_window[number] && NSEqualRects (rect, old_rect[number]))
     return;
@@ -649,22 +652,44 @@ _gdk_quartz_window_is_ancestor (GdkWindow *ancestor,
                                           gdk_window_get_parent (window)));
 }
 
-/* FIXME: It would be nice to have one function that takes an NSPoint
- * and flips the coords for any window.
- */
-gint 
-_gdk_quartz_window_get_inverted_screen_y (gint y)
+
+void
+_gdk_quartz_window_gdk_xy_to_xy (gint  gdk_x,
+                                 gint  gdk_y,
+                                 gint *ns_x,
+                                 gint *ns_y)
+{
+  GdkScreenQuartz *screen_quartz = GDK_SCREEN_QUARTZ (_gdk_screen);
+
+  if (ns_y)
+    *ns_y = screen_quartz->height - gdk_y + screen_quartz->min_y;
+
+  if (ns_x)
+    *ns_x = gdk_x + screen_quartz->min_x;
+}
+
+void
+_gdk_quartz_window_xy_to_gdk_xy (gint  ns_x,
+                                 gint  ns_y,
+                                 gint *gdk_x,
+                                 gint *gdk_y)
 {
-  int index;
-  GdkRectangle gdk_rect;
-  NSScreen *main_screen = [NSScreen mainScreen];
-  NSRect rect = [main_screen frame];
+  GdkScreenQuartz *screen_quartz = GDK_SCREEN_QUARTZ (_gdk_screen);
 
-  index = [[NSScreen screens] indexOfObject:main_screen];
+  if (gdk_y)
+    *gdk_y = screen_quartz->height - ns_y + screen_quartz->min_y;
 
-  gdk_screen_get_monitor_geometry (_gdk_screen, index, &gdk_rect);
+  if (gdk_x)
+    *gdk_x = ns_x - screen_quartz->min_x;
+}
 
-  return gdk_rect.height - y + rect.origin.y + gdk_rect.y;
+void
+_gdk_quartz_window_nspoint_to_gdk_xy (NSPoint  point,
+                                      gint    *x,
+                                      gint    *y)
+{
+  _gdk_quartz_window_xy_to_gdk_xy (point.x, point.y,
+                                   x, y);
 }
 
 static GdkWindow *
@@ -780,8 +805,7 @@ generate_motion_event (GdkWindow *window)
 
   screen_point = [NSEvent mouseLocation];
 
-  x_root = screen_point.x;
-  y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
+  _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &x_root, &y_root);
 
   point = [nswindow convertScreenToBase:screen_point];
 
@@ -860,6 +884,32 @@ _gdk_quartz_window_did_resign_main (GdkWindow *window)
   clear_toplevel_order ();
 }
 
+static NSScreen *
+get_nsscreen_for_x (gint x)
+{
+  int i;
+  NSArray *screens;
+
+  GDK_QUARTZ_ALLOC_POOL;
+
+  screens = [NSScreen screens];
+
+  for (i = 0; i < [screens count]; i++)
+    {
+      NSRect rect = [[screens objectAtIndex:i] frame];
+
+      /* FIXME: Only horizontal layouts supported for now.  Also
+       * see comments in gdkscreen-quartz.c
+       */
+      if (x >= rect.origin.x && x <= rect.origin.x + rect.size.width)
+        return [screens objectAtIndex:i];
+    }
+
+  GDK_QUARTZ_RELEASE_POOL;
+
+  return NULL;
+}
+
 void
 _gdk_window_impl_new (GdkWindow     *window,
                      GdkWindow     *real_parent,
@@ -944,12 +994,26 @@ _gdk_window_impl_new (GdkWindow     *window,
     case GDK_WINDOW_DIALOG:
     case GDK_WINDOW_TEMP:
       {
+        NSScreen *screen;
+        NSRect screen_rect;
         NSRect content_rect;
         int style_mask;
+        int nx, ny;
         const char *title;
 
-        content_rect = NSMakeRect (private->x,
-                                   _gdk_quartz_window_get_inverted_screen_y (private->y) - private->height,
+        /* initWithContentRect will place on the mainScreen by default.
+         * We want to select the screen to place on ourselves.  We need
+         * to find the screen the window will be on and correct the
+         * content_rect coordinates to be relative to that screen.
+         */
+        _gdk_quartz_window_gdk_xy_to_xy (private->x, private->y, &nx, &ny);
+
+        screen = get_nsscreen_for_x (nx);
+        screen_rect = [screen frame];
+        nx -= screen_rect.origin.x;
+        ny -= screen_rect.origin.y;
+
+        content_rect = NSMakeRect (nx, ny - private->height,
                                    private->width,
                                    private->height);
 
@@ -969,7 +1033,8 @@ _gdk_window_impl_new (GdkWindow     *window,
        impl->toplevel = [[GdkQuartzWindow alloc] initWithContentRect:content_rect 
                                                            styleMask:style_mask
                                                              backing:NSBackingStoreBuffered
-                                                               defer:NO];
+                                                               defer:NO
+                                                                screen:screen];
 
        if (attributes_mask & GDK_WA_TITLE)
          title = attributes->title;
@@ -1323,10 +1388,12 @@ move_resize_window_internal (GdkWindow *window,
     {
       NSRect content_rect;
       NSRect frame_rect;
+      gint gx, gy;
 
-      content_rect =  NSMakeRect (private->x,
-                                  _gdk_quartz_window_get_inverted_screen_y (private->y + private->height),
-                                  private->width, private->height);
+      _gdk_quartz_window_gdk_xy_to_xy (private->x, private->y + private->height,
+                                       &gx, &gy);
+
+      content_rect = NSMakeRect (gx, gy, private->width, private->height);
 
       frame_rect = [impl->toplevel frameRectForContentRect:content_rect];
       [impl->toplevel setFrame:frame_rect display:YES];
@@ -1714,10 +1781,9 @@ gdk_window_quartz_get_geometry (GdkWindow *window,
        */
       if ([impl->toplevel styleMask] == NSBorderlessWindowMask)
         {
-          if (x)
-            *x = ns_rect.origin.x;
-          if (y)
-            *y = _gdk_quartz_window_get_inverted_screen_y (ns_rect.origin.y + ns_rect.size.height);
+          _gdk_quartz_window_xy_to_gdk_xy (ns_rect.origin.x,
+                                           ns_rect.origin.y + ns_rect.size.height,
+                                           x, y);
         }
       else 
         {
@@ -1790,8 +1856,12 @@ gdk_window_quartz_get_root_coords (GdkWindow *window,
 
   content_rect = [impl->toplevel contentRectForFrameRect:[impl->toplevel frame]];
 
-  tmp_x = x + content_rect.origin.x;
-  tmp_y = y + _gdk_quartz_window_get_inverted_screen_y (content_rect.origin.y + content_rect.size.height);
+  _gdk_quartz_window_xy_to_gdk_xy (content_rect.origin.x,
+                                   content_rect.origin.y + content_rect.size.height,
+                                   &tmp_x, &tmp_y);
+
+  tmp_x += x;
+  tmp_y += y;
 
   while (private != GDK_WINDOW_OBJECT (toplevel))
     {
@@ -1870,8 +1940,7 @@ gdk_window_quartz_get_pointer_helper (GdkWindow       *window,
   if (window == _gdk_root)
     {
       point = [NSEvent mouseLocation];
-      x_tmp = point.x;
-      y_tmp = _gdk_quartz_window_get_inverted_screen_y (point.y);
+      _gdk_quartz_window_nspoint_to_gdk_xy (point, &x_tmp, &y_tmp);
     }
   else
     {
@@ -2525,8 +2594,10 @@ gdk_window_get_frame_extents (GdkWindow    *window,
 
   ns_rect = [impl->toplevel frame];
 
-  rect->x = ns_rect.origin.x;
-  rect->y = _gdk_quartz_window_get_inverted_screen_y (ns_rect.origin.y + ns_rect.size.height);
+  _gdk_quartz_window_xy_to_gdk_xy (ns_rect.origin.x,
+                                   ns_rect.origin.y + ns_rect.size.height,
+                                   &rect->x, &rect->y);
+
   rect->width = ns_rect.size.width;
   rect->height = ns_rect.size.height;
 }